From dcde22696c165beaded2a5ce2f5dd745f442d7c6 Mon Sep 17 00:00:00 2001 From: robertl Date: Thu, 15 Sep 2005 16:25:39 +0000 Subject: [PATCH] Steve Chamberlin contributes National Geographic Topo format. --- gpsbabel/Makefile | 2 +- gpsbabel/reference/tpo-sample.tpo | Bin 0 -> 9293 bytes gpsbabel/tpo.c | 805 ++++++++++++++++++++++++++++++ gpsbabel/vecs.c | 7 + 4 files changed, 813 insertions(+), 1 deletion(-) create mode 100644 gpsbabel/reference/tpo-sample.tpo create mode 100644 gpsbabel/tpo.c diff --git a/gpsbabel/Makefile b/gpsbabel/Makefile index 35ccd4f58..ff057fe2b 100644 --- a/gpsbabel/Makefile +++ b/gpsbabel/Makefile @@ -32,7 +32,7 @@ INSTALL_TARGETDIR=/usr/local/ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \ gpsutil.o pcx.o cetus.o copilot.o gpspilot.o magnav.o \ - psp.o holux.o garmin.o tmpro.o tpg.o \ + psp.o holux.o garmin.o tmpro.o tpg.o tpo.o \ xcsv.o gcdb.o tiger.o internal_styles.o easygps.o quovadis.o \ gpilots.o saroute.o navicache.o psitrex.o geoniche.o delgpl.o \ ozi.o nmea.o text.o html.o palmdoc.o netstumbler.o hsa_ndv.o \ diff --git a/gpsbabel/reference/tpo-sample.tpo b/gpsbabel/reference/tpo-sample.tpo new file mode 100644 index 0000000000000000000000000000000000000000..6c3751e1fd8537037ac773aeca3f299839c68f54 GIT binary patch literal 9293 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 +#include +#include "jeeps/gpsmath.h" /* for datum conversions */ + +#define MYNAME "TPO" + +static char *dumpheader = NULL; +static char *output_state = NULL; + +static +arglist_t tpo_args[] = { + { "dumpheader", &dumpheader, "(0/1) Display the file header bytes", + "0", ARGTYPE_BOOL} , + { "state", &output_state, "State map format to write, default=CA", + "CA", ARGTYPE_STRING} , + {0, 0, 0, 0, 0} +}; + +static FILE *tpo_file_in; +static FILE *tpo_file_out; +static void *mkshort_handle; + +static double output_track_lon_scale; +static double output_track_lat_scale; + +static unsigned int track_out_count; +static double first_track_waypoint_lat; +static double first_track_waypoint_lon; +static double track_length; +static double last_waypoint_x; +static double last_waypoint_y; +static double last_waypoint_z; + +/*******************************************************************************/ +/* READ */ +/*******************************************************************************/ + +static int +tpo_fread(void *buff, size_t size, size_t members, FILE * fp) +{ + size_t br; + + br = fread(buff, size, members, fp); + + if (br != members) { + fatal(MYNAME ": The input file does not look like a valid .TPO file.\n"); + } + + return (br); +} + +static double +tpo_fread_double(FILE *fp) +{ + unsigned char buf[8]; + unsigned char sbuf[8]; + + tpo_fread(buf, 1, 8, fp); + le_read64(sbuf, buf); + return *(double *)sbuf; +} + +static void +tpo_fwrite_double(double x, FILE *fp) +{ + unsigned char *cptr = (unsigned char *)&x; + unsigned char cbuf[8]; + + le_read64(cbuf, cptr); + fwrite(cbuf, 8, 1, fp); +} + +/* tpo_check_version_string() + Check the first bytes of the file for a version 3.0 header. */ +static void +tpo_check_version_string() +{ + + char string_size; + char* string_buffer; + char* v3_id_string = "TOPO! Ver"; + + /* read the id string */ + tpo_fread(&string_size, 1, 1, tpo_file_in); + string_buffer = xmalloc(string_size+1); + tpo_fread(string_buffer, 1, string_size, tpo_file_in); + + /* terminate the strig */ + string_buffer[string_size] = 0; + + /* check for the presence of a 3.0-style id string */ + if (strncmp(v3_id_string, string_buffer, strlen(v3_id_string)) == 0) + { + fatal(MYNAME ": gpsbabel can only read TPO version 2.7.7 or below; this file is %s\n", string_buffer); + } + else { + /* seek back to the beginning of the file */ + fseek(tpo_file_in, -(string_size+1), SEEK_CUR); + } + +} + +static void +/* tpo_dump_header_bytes(int header_size) + Write the first header_size bytes of the file to standard output + as a C array definition. */ +tpo_dump_header_bytes(int header_size) +{ + int i; + unsigned char* buffer = (unsigned char*)xmalloc(header_size); + + tpo_fread(buffer, 1, header_size, tpo_file_in); + + printf("unsigned char header_bytes[] = {\n"); + + for (i=0; irte_name = xstrdup(buff); + + /* zoom level 1-5 visibility flags */ + tpo_fread(&buff[0], 1, 10, tpo_file_in); + + /* 8 bytes of zeros, meaning unknown */ + tpo_fread(&buff[0], 1, 8, tpo_file_in); + + /* 4 more unknown bytes, possibly sign flags for the longitude and latitude? */ + tpo_fread(&buff[0], 1, 4, tpo_file_in); + + /* read the position of the initial track point */ + /* for some very odd reason, signs on longitude are swapped */ + /* coordinates are in NAD27/CONUS datum */ + + /* 8 bytes - longitude, sign swapped */ + first_lon = tpo_fread_double(tpo_file_in); + + /* 8 bytes - latitude */ + first_lat = tpo_fread_double(tpo_file_in); + + /* swap sign before we do datum conversions */ + first_lon *= -1.0; + + /* 8 unknown bytes: seems to be some kind of bounding box info */ + tpo_fread(&buff[0], 1, 8, tpo_file_in); + + /* number of route points */ + tpo_fread(&buff[0], 1, 2, tpo_file_in); + waypoint_count = le_read16(&buff[0]); + + /* allocate temporary memory for the waypoint deltas */ + lon_delta = (short*)xmalloc(waypoint_count * sizeof(short)); + lat_delta = (short*)xmalloc(waypoint_count * sizeof(short)); + + for (j=0; jlatitude, + &waypoint_temp->longitude, + &amt, + 78); + + /* there is no elevation data for the waypoints */ + waypoint_temp->altitude = 0; + + route_add_wpt(track_temp, waypoint_temp); + } + + /* free temporary memory */ + xfree(lon_delta); + xfree(lat_delta); + } +} + +/*******************************************************************************/ +/* WRITE */ +/*******************************************************************************/ + +/* tpo_write_file_header() + Write the appropriate header for the desired TOPO! state. + + National Geographic sells about 75 different state and regional software + programs called TOPO! that use the TPO format. Each one uses a different + header data sequence. The header contains the name of the state maps, as well + as some map scaling information and other data. In most cases, you can't open + a TPO file created by a different state/regional version of TOPO! than the one + you're using yourself. When writing a TPO file, it is therefore necessary to + specify what TOPO! state product to create the file for. I believe that the + TOPO! regional products can open TPO files created by the TOPO! state products + as long as the track data is within the area covered by the regional product. + As a result, it's only necessary to decide what state product output format to + use. + + TO ADD SUPPORT FOR ANOTHER STATE: + 1. Obtain an example .tpo file generated by the state product for which you wish + to add support. National Geographic MapXchange (http://maps.nationalgeographic.com/topo/search.cfm) + is a good source of .tpo files. + 2. Run gpsbabel using the "dumpheader" option of the TPO format converter, and + specifying a dummy ouput file. For example: + gpsbabel -t -i tpo,dumpheader=1 -f sample_file.tpo -o csv -F dummy.txt + This will write a snippet of C code containing the header bytes to the shell window. + 3. Add a new if() clause to tpo_write_file_header(). Copy the header bytes definition + from the previous step. + 4. Recompile gpsbabel. + 5. You should now be able write TPO ouput in the new state's format. For example, if + you added support for Texas: + gpsbabel -t -i gpx -f input.gpx -o tpo,state="TX" -F output.tpo */ +static void +tpo_write_file_header() +{ + int i; + + /* force upper-case state name */ + for (i=0; i<(int)strlen(output_state); i++) { + output_state[i] = toupper(output_state[i]); + } + + if (strncmp("CA", output_state, 2) == 0) { + + unsigned char header_bytes[] = { + 0x18, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F, 0x72, + 0x6E, 0x69, 0x61, 0x20, 0x53, 0x68, 0x61, 0x64, + 0x65, 0x64, 0x20, 0x52, 0x65, 0x6C, 0x69, 0x65, + 0x66, 0x03, 0x43, 0x41, 0x31, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x5F, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x5C, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x27, 0x43, 0x3A, 0x5C, + 0x50, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, + 0x46, 0x69, 0x6C, 0x65, 0x73, 0x5C, 0x54, 0x4F, + 0x50, 0x4F, 0x21, 0x5C, 0x54, 0x50, 0x4F, 0x5F, + 0x44, 0x41, 0x54, 0x41, 0x5C, 0x43, 0x41, 0x5F, + 0x44, 0x30, 0x31, 0x5C, 0x20, 0x43, 0x3A, 0x5C, + 0x50, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, + 0x46, 0x69, 0x6C, 0x65, 0x73, 0x5C, 0x54, 0x4F, + 0x50, 0x4F, 0x21, 0x5C, 0x54, 0x50, 0x4F, 0x5F, + 0x44, 0x41, 0x54, 0x41, 0x5C, 0x12, 0x43, 0x3A, + 0x5C, 0x54, 0x4F, 0x50, 0x4F, 0x21, 0x5C, 0x54, + 0x50, 0x4F, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5C, + 0x00, 0x00, 0x00, 0xDC, 0x30, 0x32, 0x30, 0x32, + 0x30, 0x32, 0x30, 0x33, 0x30, 0x33, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x32, 0x30, 0x32, 0x30, 0x32, + 0x30, 0x33, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x34, 0x30, 0x34, 0x30, 0x34, 0x30, 0x34, + 0x30, 0x36, 0x30, 0x36, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x35, 0x30, 0x35, 0x30, 0x34, 0x30, 0x36, + 0x30, 0x36, 0x30, 0x36, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x35, + 0x30, 0x35, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37, + 0x30, 0x37, 0x30, 0x38, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x35, + 0x30, 0x35, 0x30, 0x37, 0x30, 0x37, 0x30, 0x37, + 0x30, 0x38, 0x30, 0x38, 0x30, 0x39, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x35, + 0x31, 0x30, 0x30, 0x38, 0x30, 0x38, 0x30, 0x38, + 0x30, 0x39, 0x30, 0x39, 0x30, 0x39, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, + 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, + 0x30, 0x39, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x30, 0x39, 0x30, 0x39, + 0x0D, 0x55, 0x6E, 0x69, 0x74, 0x65, 0x64, 0x20, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x5F, 0x40, 0xBC, 0x23, + 0x63, 0xB5, 0xF9, 0x3A, 0x50, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x50, 0x40, 0x22, 0xE2, + 0xE6, 0x54, 0x32, 0x28, 0x22, 0x40, 0x0A, 0x43, + 0x61, 0x6C, 0x69, 0x66, 0x6F, 0x72, 0x6E, 0x69, + 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x5F, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x5C, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x16, 0x2A, 0x47, 0x65, 0x6E, 0x65, 0x72, + 0x61, 0x6C, 0x20, 0x52, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6E, 0x63, 0x65, 0x20, 0x4D, 0x61, 0x70, + 0x00, 0x09, 0x3D, 0x00, 0x0C, 0x43, 0x41, 0x31, + 0x5F, 0x4D, 0x41, 0x50, 0x31, 0x5C, 0x53, 0x31, + 0x4C, 0xAF, 0x02, 0x15, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x24, 0x40, 0x84, 0x00, 0x78, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, + 0x2A, 0x4E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x61, + 0x6C, 0x20, 0x41, 0x74, 0x6C, 0x61, 0x73, 0x20, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0xE8, 0x32, + 0x0D, 0x00, 0x02, 0x44, 0x41, 0x23, 0x01, 0x6C, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, + 0x3F, 0x3C, 0x00, 0x3C, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x10, 0x2A, 0x35, 0x30, 0x30, + 0x4B, 0x20, 0x4D, 0x61, 0x70, 0x20, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0xC0, 0xFE, 0x04, 0x00, + 0x02, 0x44, 0x46, 0x00, 0x01, 0x40, 0x01, 0xB5, + 0x2B, 0x4C, 0x55, 0x55, 0x55, 0xD5, 0x3F, 0xB5, + 0x2B, 0x4C, 0x55, 0x55, 0x55, 0xD5, 0x3F, 0x28, + 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x10, 0x2A, 0x31, 0x30, 0x30, 0x4B, 0x20, + 0x4D, 0x61, 0x70, 0x20, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x50, 0xC3, 0x00, 0x00, 0x02, 0x44, + 0x4B, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xB0, 0x3F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xB0, 0x3F, 0x2D, 0x00, 0x2D, + 0x00, 0x0C, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x10, + 0x2A, 0x37, 0x2E, 0x35, 0x27, 0x20, 0x4D, 0x61, + 0x70, 0x20, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x0F, 0x3C, 0x00, 0x00, 0x02, 0x44, 0x51, 0x00, + 0x00, 0x00, 0x01, 0x9A, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x3F, 0x9A, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x89, 0x3F, 0x5A, 0x00, 0x2D, 0x00, 0x0D, + 0x00, 0x01, 0x00, 0x28, 0x00 + }; + + fwrite(header_bytes, sizeof(header_bytes), 1, tpo_file_out); + } + else if (strncmp("CT", output_state, 2) == 0 || + strncmp("MA", output_state, 2) == 0 || + strncmp("ME", output_state, 2) == 0 || + strncmp("NJ", output_state, 2) == 0 || + strncmp("NH", output_state, 2) == 0 || + strncmp("NY", output_state, 2) == 0 || + strncmp("RI", output_state, 2) == 0 || + strncmp("VT", output_state, 2) == 0) { + /* These 8 states are all covered in a single "Northeast" title */ + + unsigned char header_bytes[] = { + 0x1E, 0x4E, 0x6F, 0x72, 0x74, 0x68, 0x65, 0x61, + 0x73, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x55, 0x53, + 0x41, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x64, + 0x20, 0x52, 0x65, 0x6C, 0x69, 0x65, 0x66, 0x03, + 0x4E, 0x45, 0x31, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x54, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x48, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x50, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x43, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0B, 0x44, 0x3A, 0x5C, 0x4E, 0x45, + 0x31, 0x5F, 0x44, 0x30, 0x31, 0x5C, 0x12, 0x43, + 0x3A, 0x5C, 0x54, 0x4F, 0x50, 0x4F, 0x21, 0x5C, + 0x54, 0x50, 0x4F, 0x5F, 0x44, 0x41, 0x54, 0x41, + 0x5C, 0x12, 0x45, 0x3A, 0x5C, 0x54, 0x4F, 0x50, + 0x4F, 0x21, 0x5C, 0x54, 0x50, 0x4F, 0x5F, 0x44, + 0x41, 0x54, 0x41, 0x5C, 0x00, 0x00, 0x00, 0xFF, + 0x18, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x37, + 0x30, 0x37, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x37, 0x30, 0x37, 0x30, 0x37, 0x30, 0x37, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, 0x30, 0x34, + 0x30, 0x35, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37, + 0x30, 0x37, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x30, 0x33, + 0x30, 0x34, 0x30, 0x34, 0x30, 0x35, 0x30, 0x35, + 0x30, 0x36, 0x30, 0x36, 0x30, 0x36, 0x30, 0x36, + 0x30, 0x36, 0x30, 0x32, 0x30, 0x32, 0x30, 0x32, + 0x30, 0x33, 0x30, 0x33, 0x30, 0x34, 0x30, 0x34, + 0x30, 0x35, 0x30, 0x35, 0x30, 0x36, 0x30, 0x36, + 0x30, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, + 0x30, 0x32, 0x30, 0x32, 0x30, 0x33, 0x30, 0x33, + 0x30, 0x38, 0x30, 0x38, 0x30, 0x38, 0x30, 0x39, + 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x32, 0x30, 0x32, 0x30, 0x32, + 0x30, 0x33, 0x31, 0x30, 0x31, 0x30, 0x30, 0x38, + 0x30, 0x39, 0x30, 0x39, 0x30, 0x39, 0x30, 0x39, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, + 0x31, 0x30, 0x31, 0x30, 0x30, 0x39, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x0D, 0x55, 0x6E, 0x69, 0x74, 0x65, + 0x64, 0x20, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x5F, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x4E, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x50, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x40, + 0x10, 0x4E, 0x6F, 0x72, 0x74, 0x68, 0x65, 0x61, + 0x73, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x55, 0x53, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x48, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x50, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, + 0x40, 0x16, 0x2A, 0x47, 0x65, 0x6E, 0x65, 0x72, + 0x61, 0x6C, 0x20, 0x52, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6E, 0x63, 0x65, 0x20, 0x4D, 0x61, 0x70, + 0x00, 0x09, 0x3D, 0x00, 0x0C, 0x4E, 0x45, 0x31, + 0x5F, 0x4D, 0x41, 0x50, 0x31, 0x5C, 0x53, 0x31, + 0x4C, 0x68, 0x03, 0x16, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2C, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x24, 0x40, 0x8C, 0x00, 0x64, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, + 0x2A, 0x4E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x61, + 0x6C, 0x20, 0x41, 0x74, 0x6C, 0x61, 0x73, 0x20, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0xE8, 0x32, + 0x0D, 0x00, 0x02, 0x44, 0x41, 0x0B, 0x01, 0x6C, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, + 0x3F, 0x3C, 0x00, 0x3C, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x10, 0x2A, 0x35, 0x30, 0x30, + 0x4B, 0x20, 0x4D, 0x61, 0x70, 0x20, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0xC0, 0xFE, 0x04, 0x00, + 0x02, 0x44, 0x46, 0xEA, 0x00, 0x40, 0x01, 0xB5, + 0x2B, 0x4C, 0x55, 0x55, 0x55, 0xD5, 0x3F, 0xB5, + 0x2B, 0x4C, 0x55, 0x55, 0x55, 0xD5, 0x3F, 0x28, + 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x10, 0x2A, 0x31, 0x30, 0x30, 0x4B, 0x20, + 0x4D, 0x61, 0x70, 0x20, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x50, 0xC3, 0x00, 0x00, 0x02, 0x44, + 0x4B, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xB0, 0x3F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xB0, 0x3F, 0x2D, 0x00, 0x2D, + 0x00, 0x0C, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x10, + 0x2A, 0x37, 0x2E, 0x35, 0x27, 0x20, 0x4D, 0x61, + 0x70, 0x20, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x0F, 0x3C, 0x00, 0x00, 0x02, 0x44, 0x51, 0x00, + 0x00, 0x00, 0x01, 0x9A, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x3F, 0x9A, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x89, 0x3F, 0x5A, 0x00, 0x2D, 0x00, 0x0D, + 0x00, 0x01, 0x00, 0x28, 0x00 + }; + + fwrite(header_bytes, sizeof(header_bytes), 1, tpo_file_out); + } + + else { + fatal(MYNAME ": writing ouput for state \"%s\" is not currently supported.\n", output_state); + } +} + +static void +tpo_track_hdr(const route_head *rte) +{ + double amt; + unsigned char temp_buffer[8]; + unsigned char visibility_flags[] = { 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00 }; + unsigned char unknown1[] = { 0xFF, 0x00, 0x00, 0x00 }; + unsigned char bounding_box[8] = { 0x00, 0x80, 0x00, 0x80, 0xFF, 0x7F, 0xFF, 0x7F }; + + waypoint* first_track_waypoint = (waypoint*) QUEUE_FIRST(&rte->waypoint_list); + + /* zoom level 1-5 visibility flags */ + fwrite(visibility_flags, 1, sizeof(visibility_flags), tpo_file_out); + + /* 8 bytes of zeros, meaning unknown */ + memset(temp_buffer, 0, sizeof(temp_buffer)); + fwrite(temp_buffer, 1, sizeof(temp_buffer), tpo_file_out); + + /* 4 more unknown bytes, possibly sign flags for the longitude and latitude? */ + fwrite(unknown1, 1, sizeof(unknown1), tpo_file_out); + + /* the starting point of the route */ + /* convert lat/long to NAD27/CONUS datum */ + GPS_Math_WGS84_To_Known_Datum_M( + first_track_waypoint->latitude, + first_track_waypoint->longitude, + first_track_waypoint->altitude, + &first_track_waypoint_lat, + &first_track_waypoint_lon, + &amt, + 78); + + /* swap the sign back *after* the datum conversion */ + first_track_waypoint_lon *= -1.0; + + /* Compute this track's scaling factors: Used for scaling each track point and then + later written out to the track footer. These are approximately the ratios between + pixels and degrees when viewing the 1:24000 map in TOPO!. In practice, it doesn't + appear to be necessary that they be correct, as long as the same values are used + for doing the scaling and for writing into the track footer data. */ + output_track_lat_scale = 4.8828125e-005; /* TOPO! appears to use a constant lat scale */ + output_track_lon_scale = output_track_lat_scale / cos(GPS_Math_Deg_To_Rad(first_track_waypoint_lat)); + + /* 8 bytes - longitude */ + tpo_fwrite_double(first_track_waypoint_lon, tpo_file_out); + + /* 8 bytes - latitude */ + tpo_fwrite_double(first_track_waypoint_lat, tpo_file_out); + + /* 8 bytes: seems to be bounding box info */ + fwrite(bounding_box, 1, sizeof(bounding_box), tpo_file_out); + + /* number of route points */ + le_write16(temp_buffer, rte->rte_waypt_ct); + fwrite(temp_buffer, 1, 2, tpo_file_out); + + /* initialize the track length computation */ + track_length = 0; + GPS_Math_WGS84LatLonH_To_XYZ( + first_track_waypoint->latitude, + first_track_waypoint->longitude, + 0.0, + &last_waypoint_x, + &last_waypoint_y, + &last_waypoint_z); +} + +static void +tpo_track_disp(const waypoint *waypointp) +{ + double lat, lon, amt, x, y, z; + short lat_delta, lon_delta; + unsigned char temp_buffer[2]; + + /* convert lat/lon position to XYZ meters */ + GPS_Math_WGS84LatLonH_To_XYZ( + waypointp->latitude, + waypointp->longitude, + 0.0, + &x, + &y, + &z); + + /* increase the track length by the 3D length of last track segment in feet */ + track_length += 3.2808 * sqrt( + (x - last_waypoint_x) * (x - last_waypoint_x) + + (y - last_waypoint_y) * (y - last_waypoint_y) + + (z - last_waypoint_z) * (z - last_waypoint_z)); + last_waypoint_x = x; + last_waypoint_y = y; + last_waypoint_z = z; + + /* convert lat/long to NAD27/CONUS datum */ + GPS_Math_WGS84_To_Known_Datum_M( + waypointp->latitude, + waypointp->longitude, + waypointp->altitude, + &lat, + &lon, + &amt, + 78); + + /* swap the sign back *after* the datum conversion */ + lon *= -1.0; + + /* longitude delta from first route point */ + lon_delta = (short)((first_track_waypoint_lon - lon) / output_track_lon_scale); + le_write16(temp_buffer, lon_delta); + fwrite(temp_buffer, 1, 2, tpo_file_out); + + /* latitude delta from first route point */ + lat_delta = (short)((first_track_waypoint_lat - lat) / output_track_lat_scale); + le_write16(temp_buffer, lat_delta); + fwrite(temp_buffer, 1, 2, tpo_file_out); +} + +static void +tpo_track_tlr(const route_head *rte) +{ + unsigned char temp_buffer[2]; + + unsigned char unknown1[] = { 0x06, 0x00 }; + + unsigned char continue_marker[] = { 0x01, 0x80 }; + unsigned char end_marker[] = { 0x00, 0x00 }; + + /* pixel to degree scaling factors */ + tpo_fwrite_double(output_track_lon_scale, tpo_file_out); + tpo_fwrite_double(output_track_lat_scale, tpo_file_out); + + /* 4 bytes: the total length of the route */ + le_write32(temp_buffer, (unsigned int)track_length); + fwrite(temp_buffer, 1, 4, tpo_file_out); + + /* 2 unknown bytes */ + fwrite(unknown1, 1, sizeof(unknown1), tpo_file_out); + + /* the last track ends with 0x0000 instead of 0x0180 */ + track_out_count++; + if (track_out_count == track_count()) { + fwrite(end_marker, 1, sizeof(end_marker), tpo_file_out); + } else { + fwrite(continue_marker, 1, sizeof(continue_marker), tpo_file_out); + } +} + +static void +tpo_wr_init(const char *fname) +{ + if (doing_wpts || doing_rtes) + { + fatal(MYNAME ": this file format only supports tracks, not waypoints or routes.\n"); + } + + tpo_file_out = xfopen(fname, "wb", MYNAME); + tpo_write_file_header(); +} + +static void +tpo_wr_deinit(void) +{ + /* the file footer is six bytes of zeroes */ + unsigned char file_footer_bytes[6]; + memset(file_footer_bytes, 0, sizeof(file_footer_bytes)); + fwrite(file_footer_bytes, 1, sizeof(file_footer_bytes), tpo_file_out); + + fclose(tpo_file_out); +} + +static void +tpo_write(void) +{ + unsigned char buffer[8]; + unsigned char unknown1[] = { 0xFF, 0xFF, 0x01, 0x00 }; + + char* chunk_name = "CTopoRoute"; + int chunk_name_length = strlen(chunk_name); + + /* write the total number of tracks */ + le_write16(buffer, track_count()); + fwrite(buffer, 1, 2, tpo_file_out); + + /* 4 unknown bytes */ + fwrite(unknown1, 1, 4, tpo_file_out); + + /* chunk name: "CTopoRoute" */ + le_write16(buffer, chunk_name_length); + fwrite(buffer, 1, 2, tpo_file_out); + fwrite(chunk_name, 1, chunk_name_length, tpo_file_out); + + track_out_count = 0; + track_disp_all(tpo_track_hdr, tpo_track_tlr, tpo_track_disp); +} + +/* TPO format can read and write tracks only */ +ff_vecs_t tpo_vecs = { + ff_type_file, + { ff_cap_none | ff_cap_none, ff_cap_read | ff_cap_write, ff_cap_none | ff_cap_none }, + tpo_rd_init, + tpo_wr_init, + tpo_rd_deinit, + tpo_wr_deinit, + tpo_read, + tpo_write, + NULL, + tpo_args, + CET_CHARSET_ASCII, 0 /* CET-REVIEW */ +}; diff --git a/gpsbabel/vecs.c b/gpsbabel/vecs.c index dcb9c052c..84090e104 100644 --- a/gpsbabel/vecs.c +++ b/gpsbabel/vecs.c @@ -49,6 +49,7 @@ extern ff_vecs_t garmin_vecs; extern ff_vecs_t holux_vecs; extern ff_vecs_t xcsv_vecs; extern ff_vecs_t tpg_vecs; +extern ff_vecs_t tpo_vecs; extern ff_vecs_t magnav_vec; extern ff_vecs_t tmpro_vecs; extern ff_vecs_t gcdb_vecs; @@ -210,6 +211,12 @@ vecs_t vec_list[] = { "National Geographic Topo .tpg", "tpg" }, + { + &tpo_vecs, + "tpo", + "National Geographic Topo .tpo (tracks)", + "tpo" + }, { &tmpro_vecs, "tmpro", -- 2.30.2